﻿/// <summary>
/// Create By:  Alex Sanchez Sanchez
/// Date:       19/11/2012
/// Time:       14:50
/// Update:     
/// Time Update: 
/// Fix Bugs Descripion:
/// 
/// </summary>

using System;
using System.Data;
using System.Linq;
using Npgsql;
using NpgsqlTypes ;

namespace LayerData.DataBaseManager
{
    /// <summary>
    /// Clase que permite el acceso a datos.
    /// </summary>
    internal partial class PostgreSqlHelper
    {
        #region Constructor
        public PostgreSqlHelper()
        {
        }

        #endregion

        #region Variables
        static readonly string strMensajeErrorComando = string.Concat(string.Format("Error al Procosar el Comando, revise lo Siguiente: {0}", Environment.NewLine));
        #endregion

        #region Metodos Privados de Ayuda

        /// <summary>
        /// Este método se utiliza para asignar la coleccion de <see cref="NpgsqlParameter"/> a un <see cref="NpgsqlCommand"/>.
        /// 
        /// Este metodo asigna un valor de DbNull a cualquier parametro que sea del tipo InputOutput.
        /// 
        /// Este metodo evita que los valores por defecto se actualizen, 
        /// pero este es el caso menos común que un parámetro de salida (derived como InputOutput) 
        /// donde el usuario no ha proporcionado un valor de entrada.
        /// </summary>
        /// <param name="command">Comando al cual seran agregados los parametros</param>
        /// <param name="parameter">Coleccion de parametros a agregar al comando.</param>
        private static void AttachParameters(NpgsqlCommand command, NpgsqlParameter[] parameter)
        {
            foreach (NpgsqlParameter p in parameter)
            {
                if ((p.Direction == ParameterDirection.InputOutput) && (p.Value == null))
                {
                    p.Value = DBNull.Value;
                }
                command.Parameters.Add(p);
            }
        }

        /// <summary>
        /// Este método asigna un array de valores a una coleccion de <see cref="NpgsqlParameter"/>.
        /// </summary>
        /// <param name="commandParameters">Coleccion de parametros a Asignar su valor</param>
        /// <param name="parameterValues">Colecion de valores a asignar al <paramref name="commandParameters"/></param>
        private static void AssignParameterValues(NpgsqlParameter[] commandParameters, object[] parameterValues)
        {
            if ((commandParameters == null) || (parameterValues == null))
            {
                return;
            }

            // El numero de parametros debe ser = al numero de Valores pasados.
            if (commandParameters.Length != parameterValues.Length)
            {
                Console.Write("parametes"+commandParameters.Length+"\n");
                Console.Write("Values"+commandParameters.Length);
                throw new ArgumentException("Número de parámetro no corresponde al número de parámetro esperado.");
            }

            //Asignamos los valores en la posicion correspondiente.
            for (int i = 0, j = commandParameters.Length; i < j; i++)
            {
                commandParameters[i].Value = parameterValues[i];
            }
        }

        /// <summary>
        /// Este metodo abre (si es necesario) y asigna la conexion , transaccion, tipo de comando y parametros al comando establecido.
        /// </summary>
        /// <param name="command">El <see cref="NpgsqlCommand"/> a Preparar.</param>
        /// <param name="connection">Un <see cref="NpgsqlConnection"/> valido, para ejecutar el comando.</param>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> valido, o Null</param>
        /// <param name="commandType">El Comando (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del procedimiento Almacenado o un comando T-SQL</param>
        /// <param name="commandParameters">Coleccion de <see cref="NpgsqlParameter"/>  asociados al comando o 
        /// null si el <see cref="NpgsqlCommand"/> no tiene parametros
        /// </param>
        private static void PrepareCommand(NpgsqlCommand command, NpgsqlConnection connection, NpgsqlTransaction transaction, CommandType commandType, string commandText, NpgsqlParameter[] commandParameters)
        {
            //Si la conexion no esta abierta, lo abrimos
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }

            //Asociamos la conexión con el comando
            command.Connection = connection;

            //establecemos el Comando (stored procedure o un Comando T-SQL)
            command.CommandText = commandText;

            //si nos dieron una transacción, lo asignamos.
            if (transaction != null)
            {
                command.Transaction = transaction;
            }

            //Establecemos el Command Type
            command.CommandType = commandType;

            //Agregamos los parametros, si es necesario.
            if (commandParameters != null)
            {
                AttachParameters(command, commandParameters);
            }

            return;
        }

        #endregion

        #region ExecuteNonQuery

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/>.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int result = ExecuteNonQuery(connection, CommandType.StoredProcedure, "PublishOrders", new NpgsqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connection">Un <see cref="NpgsqlConnection"/> Valido</param>
        /// <param name="commandType">El tipo de comando(stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o un Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="Int"/> que representa el numero de filas afectadas por el comando</returns>
        public static int ExecuteNonQuery(NpgsqlConnection connection, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            int retval = 0;
            NpgsqlCommand cmd = new NpgsqlCommand();

            try
            {
                //crear un comando y prepararlo para su ejecución
                PrepareCommand(cmd, connection, (NpgsqlTransaction)null, commandType, commandText, commandParameters);
                //ejecutar el comando.
                retval = cmd.ExecuteNonQuery();
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                //Limpiamos la coleccion de parametros del Command
                cmd.Parameters.Clear();
            }
            return retval;
        }
        
        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/>  en la base de datos especificada, usando los parametros establecidos.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int result = ExecuteNonQuery(IPostgreSqlConnectionInfo, CommandType.StoredProcedure, "PublishOrders", new NpgsqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="IPostgreSqlConnectionInfo"/> valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a usar con el Comando</param>
        /// <returns>Un <see cref="Int"/> que representa el numero de filas afectadas por el comando</returns>
        public static int ExecuteNonQuery(IPostgreSqlConnectionInfo connectionInfo, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            NpgsqlConnection con = connectionInfo.GetPostgreSqlConnection();
            int result = 0;
            try
            {
                connectionInfo.OpenConnection(con);
                result = ExecuteNonQuery(con, commandType, commandText, commandParameters);
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                connectionInfo.CloseConnection(con);
            }
            return result;
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/>.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int result = ExecuteNonQuery(connectionInfo, CommandType.StoredProcedure, "PublishOrders");
        /// </remarks>
        /// <param name="connectionInfo">un <see cref="IPostgreSqlConnectionInfo"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <returns>Un <see cref="Int"/> que representa el numero de filas afectadas por el comando</returns>
        public static int ExecuteNonQuery(IPostgreSqlConnectionInfo connectionInfo, CommandType commandType, string commandText)
        {
            return ExecuteNonQuery(connectionInfo, commandType, commandText, (NpgsqlParameter[])null);
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> (no devuelve ningún resultados).
        /// Este metodo consulta a la Base de datos la informacion de los parametros para el 
        /// Procedimiento Almacenado(La primera vez que es llamado),  y asigna los valos en el order adecuado.
        /// </summary>
        /// <remarks>
        /// Este metodo no provee acceso a los parametros de Salida(output parameters).
        /// 
        /// Ejemplo:  
        ///  int result = ExecuteNonQuery(connectionInfo, "PublishOrders", 24, 36);
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="IPostgreSqlConnectionInfo"/> Valido</param>
        /// <param name="spName">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="parameterValues">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="Int"/> que representa el numero de filas afectadas por el comando</returns>
        public static int ExecuteNonQuery(IPostgreSqlConnectionInfo connectionInfo, string spName, params object[] parameterValues)
        {
            //si recibimos valores de los parámetros.
            if ((parameterValues != null) && (parameterValues.Length > 0))
            {
                //extraer los parámetros para este procedimiento almacenado 
                //desde la memoria caché de parámetros (o de la base de datos y cargarlos a la memoria caché)
                NpgsqlParameter[] commandParameters = PostgreSqlHelperParameterCache.GetSpParameterSet(connectionInfo, spName);

                //asignar los valores proporcionados a estos parámetros en base a orden de los parámetros
                AssignParameterValues(commandParameters, parameterValues);

                return ExecuteNonQuery(connectionInfo, CommandType.StoredProcedure, spName, commandParameters);
            }
            else
            {
                return ExecuteNonQuery(connectionInfo, CommandType.StoredProcedure, spName);
            }
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> utilizando un <see cref="NpgsqlTransaction"/> especificado. 
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int result = ExecuteNonQuery(transaction, CommandType.StoredProcedure, "PublishOrders");
        /// </remarks>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <returns>Un <see cref="Int"/> que representa el numero de filas afectadas por el comando</returns>
        public static int ExecuteNonQuery(NpgsqlTransaction transaction, CommandType commandType, string commandText)
        {
            return ExecuteNonQuery(transaction, commandType, commandText, (NpgsqlParameter[])null);
        }
        
        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> utilizando un <see cref="NpgsqlTransaction"/> especificado. 
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int result = ExecuteNonQuery(transaction, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="Int"/> que representa el numero de filas afectadas por el comando</returns>
        public static int ExecuteNonQuery(NpgsqlTransaction transaction, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            //Creamos y preparamos el comando a ejecutar
            NpgsqlCommand cmd = new NpgsqlCommand();
            int retval = 0;
            try
            {
                PrepareCommand(cmd, transaction.Connection, transaction, commandType, commandText, commandParameters);

                //Ejecutamos el comado.
                retval = cmd.ExecuteNonQuery();
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                // separar los SqlParameters desde el objeto de comando, para que puedan ser utilizados de nuevo.
                cmd.Parameters.Clear();
            }
            
            return retval;
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> utilizando un <see cref="NpgsqlTransaction"/> especificado. 
        /// Este metodo consulta a la Base de datos, para obtener la infromacion de los parametros para el 
        /// Procedimiento Almacenado(La primera vez que es llamado),  y asigna los valos en el order adecuado.
        /// </summary>
        /// <remarks>
        /// Este metodo no provee acceso a los parametros de Salida(output parameters).
        /// 
        /// Ejemplo:  
        ///  int result = ExecuteNonQuery(connectionInfo, transaction, "PublishOrders", 24, 36);
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="IPostgreSqlConnectionInfo"/> Valido</param>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> Valido</param>
        /// <param name="spName">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="parameterValues">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="Int"/> que representa el numero de filas afectadas por el comando</returns>
        public static int ExecuteNonQuery(IPostgreSqlConnectionInfo connectionInfo, NpgsqlTransaction transaction, string spName, params object[] parameterValues)
        {
            //si recibimos valores de los parámetros
            if ((parameterValues != null) && (parameterValues.Length > 0))
            {
                //Obtenemos los parametros del procedimiento almacenado
                NpgsqlParameter[] commandParameters = PostgreSqlHelperParameterCache.GetSpParameterSet(connectionInfo, spName);

                //Asignamos los valores de los parametros enel order que aparecen
                AssignParameterValues(commandParameters, parameterValues);

                return ExecuteNonQuery(transaction, CommandType.StoredProcedure, spName, commandParameters);
            }
            else
            {
                return ExecuteNonQuery(transaction, CommandType.StoredProcedure, spName);
            }
        }

        #endregion

        #region ExecuteDataSet

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> y retorna un DataSet con los resultados.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  DataSet ds = ExecuteDataset(connectionInfo, CommandType.StoredProcedure, "GetOrders");
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="IPostgreSqlConnectionInfo"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <returns>Un DataSet con los resultados generados por el <see cref="NpgsqlCommand"/></returns>
        public static DataSet ExecuteDataset(IPostgreSqlConnectionInfo connectionInfo, CommandType commandType, string commandText)
        {
            return ExecuteDataset(connectionInfo, commandType, commandText, (NpgsqlParameter[])null);
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> y retorna un DataSet con los resultados, utilizando los parametros indicados.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  DataSet ds = ExecuteDataset(connString, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="IPostgreSqlConnectionInfo"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="commandParameters">Un <see cref="NpgsqlParameter"/> con los parametros</param>
        /// <returns>Un DataSet con los resultados generados por el <see cref="NpgsqlCommand"/></returns>
        public static DataSet ExecuteDataset(IPostgreSqlConnectionInfo connectionInfo, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            NpgsqlConnection con = connectionInfo.GetPostgreSqlConnection();
            DataSet ds = new DataSet();
            try
            {
                connectionInfo.OpenConnection(con);
                ds = ExecuteDataset(con, commandType, commandText, commandParameters);
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                connectionInfo.CloseConnection(con);
            }
            return ds;
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/>  en la base de datos especificada, usando los parametros establecidos.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  DataSet ds = ExecuteDataset(connection, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connection">Un <see cref="NpgsqlConnection"/> Valido</param>
        /// <param name="commandType">El tipo de comando(stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o un Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="DataSet"/> con los resultados generados por el comando</returns>
        public static DataSet ExecuteDataset(NpgsqlConnection connection, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            DataSet ds = new DataSet();
            NpgsqlCommand cmd = new NpgsqlCommand();
            try
            {
                //Creamos y preparamos el comando                
                PrepareCommand(cmd, connection, (NpgsqlTransaction)null, commandType, commandText, commandParameters);
                //Creamos el NpgsqlDataAdapter  y el DataSet
                NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmd);
                //Llenamos el DataSet
                da.Fill(ds);
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                cmd.Parameters.Clear();
            }
            return ds;
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> , usando los parametros establecidos.
        /// Este metodo consulta a la base de datos la informacion de los parametros a utilizar en el SP
        /// (La primera vez que el SP es llamado),  y asignado en el order adecuado.
        /// </summary>
        /// <remarks>
        /// Este metodo no provee acceso a los parametros del tipo Output.
        /// 
        /// Ejemplo:  
        ///  DataSet ds = ExecuteDataset(conn, "GetOrders", 24, 36);
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="NpgsqlConnection"/> Valido</param>
        /// <param name="spName">El nombre del Procedimiento almacenado o un Comando T-SQL</param>
        /// <param name="parameterValues">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="DataSet"/> con los resultados generados por el comando</returns>
        public static DataSet ExecuteDataset(IPostgreSqlConnectionInfo connectionInfo, string spName, params object[] parameterValues)
        {
            //Si recibimos parametros, asignamos los valores
            if ((parameterValues != null) && (parameterValues.Length > 0))
            {
                //Recuperamos los parametros de la memoria cache o recuperamos la informacion de los parametros desde la base de datos.
                NpgsqlParameter[] commandParameters = PostgreSqlHelperParameterCache.GetSpParameterSet(connectionInfo, spName);

                //Asignamos los valores a los parametros
                AssignParameterValues(commandParameters, parameterValues);

                return ExecuteDataset(connectionInfo.GetPostgreSqlConnection(), CommandType.StoredProcedure, spName, commandParameters);
            }
            else
            {
                return ExecuteDataset(connectionInfo.GetPostgreSqlConnection(), CommandType.StoredProcedure, spName);
            }
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/>, usando  el <see cref="NpgsqlTransaction"/> establecido.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  DataSet ds = ExecuteDataset(trans, CommandType.StoredProcedure, "GetOrders");
        /// </remarks>
        /// <param name="transaction">Un <see cref="NpgsqlConnection"/> Valido</param>
        /// <param name="commandType">El tipo de comando(stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o un Comando T-SQL</param>
        /// <returns>Un <see cref="DataSet"/> con los resultados generados por el comando</returns>
        public static DataSet ExecuteDataset(NpgsqlTransaction transaction, CommandType commandType, string commandText)
        {
            return ExecuteDataset(transaction, commandType, commandText, (NpgsqlParameter[])null);
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/>, usando  el <see cref="NpgsqlTransaction"/> establecido.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  DataSet ds = ExecuteDataset(trans, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="transaction">Un <see cref="NpgsqlConnection"/> Valido</param>
        /// <param name="commandType">El tipo de comando(stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o un Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="DataSet"/> con los resultados generados por el comando</returns>
        public static DataSet ExecuteDataset(NpgsqlTransaction transaction, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            //Creamos y preparamos el comando
            NpgsqlCommand cmd = new NpgsqlCommand();
            DataSet ds = new DataSet();
            try
            {
                PrepareCommand(cmd, transaction.Connection, transaction, commandType, commandText, commandParameters);
                //Creamos el  NpgsqlDataAdapter & DataSet
                NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmd);
                //Cargamos el dataset con los resultados 
                da.Fill(ds);
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                // Limpiamos los parametros del command
                cmd.Parameters.Clear();
            }
            return ds;
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/>, usando  el <see cref="NpgsqlTransaction"/> establecido usando el 
        /// proveedor de paramemtros.  Este metodo consulta a la base de datos por la informacion de los parametros del SP, 
        /// (La primera vez que es llamado ),  y asignado en el orden adecuado
        /// </summary>
        /// <remarks>
        /// Este metodo no esteblece acceso a los parametros de salida.
        /// 
        /// Ejemplo:  
        ///  DataSet ds = ExecuteDataset(trans, "GetOrders", 24, 36);
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="NpgsqlConnection"/> Valido</param>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> Valido</param>
        /// <param name="spName">El nombre del Procedimiento almacenado o un Comando T-SQL</param>
        /// <param name="parameterValues">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="DataSet"/> con los resultados generados por el comando</returns>
        public static DataSet ExecuteDataset(IPostgreSqlConnectionInfo connectionInfo, NpgsqlTransaction transaction, string spName, params object[] parameterValues)
        {
            //Si existen parametros los asignamos 
            if ((parameterValues != null) && (parameterValues.Length > 0))
            {
                //Recuperamos los parametros de la memoria cache o si no de la misma base de datos.
                NpgsqlParameter[] commandParameters = PostgreSqlHelperParameterCache.GetSpParameterSet(connectionInfo, spName);

                //Asignamos los valores a los parametros
                AssignParameterValues(commandParameters, parameterValues);

                //Ejecutamos el comando
                return ExecuteDataset(transaction, CommandType.StoredProcedure, spName, commandParameters);
            }
            else
            {
                return ExecuteDataset(transaction, CommandType.StoredProcedure, spName);
            }
        }

        #endregion

        #region ExecuteReader

        /// <summary>
        /// Crea y Prepara un <see cref="NpgsqlCommand"/> y ejecuta ExecuteReader con el CommandBehavior Apropiado.
        /// </summary>
        /// <remarks>
        /// Si se crea y se abre la conexión, queremos que la conexión se cierra cuando se cierra DataReader.
        /// 
        /// Si la persona que llama proporciona la conexión, queremos dejar en manos de ellos la Conexion.
        /// </remarks>
        /// <param name="connection">Un <see cref=" NpgsqlConnection"/>, valido para ejecutar este comando</param>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> valido, or null si nu se va a utilizar</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a usar con el Comando</param>
        /// <param name="connectionOwnership">Indicia quie administrara la conexion.</param>
        /// <returns>Un <see cref="NpgsqlDataReader"/> con los resultados.</returns>
        public static NpgsqlDataReader ExecuteReader(NpgsqlConnection connection, NpgsqlTransaction transaction, CommandType commandType, string commandText, NpgsqlParameter[] commandParameters, SqlConnectionOwnership connectionOwnership)
        {
            //Creamos el Comando
            NpgsqlCommand cmd = new NpgsqlCommand();
            NpgsqlDataReader dr = null;
            try
            {
                //preparamos el comando a utilizar
                PrepareCommand(cmd, connection, transaction, commandType, commandText, commandParameters);
                // Ejecutamos el comando con el CommandBehavior adecuado.
                if (connectionOwnership == SqlConnectionOwnership.External)
                {
                    dr = cmd.ExecuteReader();
                }
                else
                {
                    dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                }
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                // Limpiamos la coleccion de parametros para porder utilizarlo mas adelante
                cmd.Parameters.Clear();
            }
            return dr;
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/>  sin parametros.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  SqlDataReader dr = ExecuteReader(connString, CommandType.StoredProcedure, "GetOrders");
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref=" IPostgreSqlConnectionInfo"/>, valido para ejecutar este comando</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <returns>Un <see cref="NpgsqlDataReader"/> con los resultados.</returns>
        public static NpgsqlDataReader ExecuteReader(IPostgreSqlConnectionInfo connectionInfo, CommandType commandType, string commandText)
        {
            return ExecuteReader(connectionInfo, commandType, commandText, (NpgsqlParameter[])null);
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/>  con los parametros indicados.
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  SqlDataReader dr = ExecuteReader(connString, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref=" IPostgreSqlConnectionInfo"/>, valido para ejecutar este comando</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a usar con el Comando</param>
        /// <returns>Un <see cref="NpgsqlDataReader"/> con los resultados.</returns>
        public static NpgsqlDataReader ExecuteReader(IPostgreSqlConnectionInfo connectionInfo, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            NpgsqlConnection cn = connectionInfo.GetPostgreSqlConnection();
            try
            {
                connectionInfo.OpenConnection(cn);
                return ExecuteReader(cn, null, commandType, commandText, commandParameters, SqlConnectionOwnership.Internal);
            }
            catch (NpgsqlException ex)
            {
                connectionInfo.CloseConnection(cn);
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
                throw;
            }
            finally
            { 
            }

        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> y recupera los parametros de la base de datos.  
        /// Este metodo consulta a la base de datos la informacion de los parametros para el SP
        /// (La primera vez que este es llamado),  y asignarlos en el orden adecuado.
        /// </summary>
        /// <remarks>
        /// Este metodo no provee acceso a los parametros de salida.
        /// 
        /// Ejemplo:  
        ///  SqlDataReader dr = ExecuteReader(connString, "GetOrders", 24, 36);
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref=" IPostgreSqlConnectionInfo"/>, valido para ejecutar este comando</param>
        /// <param name="spName">the name of the stored procedure</param>
        /// <param name="parameterValues">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <returns>Un <see cref="NpgsqlDataReader"/> con los resultados.</returns>
        public static NpgsqlDataReader ExecuteReader(IPostgreSqlConnectionInfo connectionInfo, string spName, params object[] parameterValues)
        {
            //Si recibimos parametros los asignamos.
            if ((parameterValues != null) && (parameterValues.Length > 0))
            {
                //Recuperamos los parametros desde la memoria cache o desde la misma base de datos.
                NpgsqlParameter[] commandParameters = PostgreSqlHelperParameterCache.GetSpParameterSet(connectionInfo, spName);

                //Asiganmos los valores a los parametros
                AssignParameterValues(commandParameters, parameterValues);

                //Ejecutamos el comando.
                return ExecuteReader(connectionInfo, CommandType.StoredProcedure, spName, commandParameters);
            }
            else
            {
                return ExecuteReader(connectionInfo, CommandType.StoredProcedure, spName);
            }
        }
        
        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> con un <see cref="NpgsqlTransaction"/>
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  SqlDataReader dr = ExecuteReader(trans, CommandType.StoredProcedure, "GetOrders");
        /// </remarks>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <returns>Un <see cref="NpgsqlDataReader"/> con los resultados.</returns>
        public static NpgsqlDataReader ExecuteReader(NpgsqlTransaction transaction, CommandType commandType, string commandText)
        {
            return ExecuteReader(transaction, commandType, commandText, (NpgsqlParameter[])null);
        }

        /// <summary>
        /// Execute a SqlCommand (that returns a resultset) against the specified SqlTransaction
        /// using the provided parameters.
        /// </summary>
        /// <remarks>
        /// e.g.:  
        ///   SqlDataReader dr = ExecuteReader(trans, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="transaction">a valid SqlTransaction</param>
        /// <param name="commandType">the CommandType (stored procedure, text, etc.)</param>
        /// <param name="commandText">the stored procedure name or T-SQL command</param>
        /// <param name="commandParameters">an array of SqlParamters used to execute the command</param>
        /// <returns>a SqlDataReader containing the resultset generated by the command</returns>
        public static NpgsqlDataReader ExecuteReader(NpgsqlTransaction transaction, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            //pass through to private overload, indicating that the connection is owned by the caller
            return ExecuteReader(transaction.Connection, transaction, commandType, commandText, commandParameters, SqlConnectionOwnership.External);
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> con un <see cref="NpgsqlTransaction"/> .
        /// Este metodo consulta a la base de datos la informacion de los parametros requeridos para el Procedimiento Almacenado. 
        /// (La primera vez que es llamado), y asigna los valores en el orden adecuado.
        /// </summary>
        /// <remarks>
        /// Este medoto no provee acceso los parametros de salida.
        /// 
        /// Ejemplo:  
        ///  SqlDataReader dr = ExecuteReader(trans, "GetOrders", 24, 36);
        /// </remarks>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> Valido</param>
        /// <param name="spName">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="parameterValues">Coleccion de valores de parametros</param>
        /// <returns>Un <see cref="NpgsqlDataReader"/> con los resultados.</returns>
        public static NpgsqlDataReader ExecuteReader(IPostgreSqlConnectionInfo connectionInfo, NpgsqlTransaction transaction, string spName, params object[] parameterValues)
        {
            //Si se recibieron parametros, los asignamos
            if ((parameterValues != null) && (parameterValues.Length > 0))
            {
                NpgsqlParameter[] commandParameters = PostgreSqlHelperParameterCache.GetSpParameterSet(connectionInfo, spName);

                AssignParameterValues(commandParameters, parameterValues);

                return ExecuteReader(transaction, CommandType.StoredProcedure, spName, commandParameters);
            }
            else
            {
                return ExecuteReader(transaction, CommandType.StoredProcedure, spName);
            }
        }

        #endregion ExecuteReader

        #region ExecuteScalar

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> (que devuelve un conjunto de resultados 1x1 y no toma ningún parámetro).
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int orderCount = (int)ExecuteScalar(connString, CommandType.StoredProcedure, "GetOrderCount");
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="IPostgreSqlConnectionInfo"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <returns>Un <see cref="object"/> que contiene el valor del conjunto de resultados 1x1 generado por el comando</returns>
        public static object ExecuteScalar(IPostgreSqlConnectionInfo connectionInfo, CommandType commandType, string commandText)
        {
            return ExecuteScalar(connectionInfo, commandType, commandText, (NpgsqlParameter[])null);
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> (que devuelve un conjunto de resultados 1x1)
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int orderCount = (int)ExecuteScalar(connString, CommandType.StoredProcedure, "GetOrderCount", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="IPostgreSqlConnectionInfo"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="object"/> que contiene el valor del conjunto de resultados 1x1 generado por el comando</returns>
        public static object ExecuteScalar(IPostgreSqlConnectionInfo connectionInfo, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            NpgsqlConnection con = connectionInfo.GetPostgreSqlConnection();
            object result = 0;
            try
            {
                connectionInfo.OpenConnection(con);
                result = ExecuteScalar(con, commandType, commandText, commandParameters);
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            catch (Exception ex) {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                connectionInfo.CloseConnection(con);
            }

            return result;
        }

        /// <summary>
        ///  Ejecuta un <see cref="NpgsqlCommand"/> (que devuelve un conjunto de resultados 1x1)
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int orderCount = (int)ExecuteScalar(conn, CommandType.StoredProcedure, "GetOrderCount", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connection">Un <see cref="NpgsqlConnection"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.)</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="object"/> que contiene el valor del conjunto de resultados 1x1 generado por el comando</returns>
        public static object ExecuteScalar(NpgsqlConnection connection, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            //Creamos y preparamos el comando
            NpgsqlCommand cmd = new NpgsqlCommand();
            object retval = null;
            try
            {
                PrepareCommand(cmd, connection, (NpgsqlTransaction)null, commandType, commandText, commandParameters);

                //Ejecutamos el comando
                retval = cmd.ExecuteScalar();
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            catch (Exception ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                // Liberamos la colecion de parametros
                cmd.Parameters.Clear();
            }
            return retval;
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> (que devuelve un conjunto de resultados 1x1).  
        /// Este metodo consultla la informacion de los parametros del Procedimiento almacenado y asigna los valores en el order adecuado.
        /// </summary>
        /// <remarks>
        /// Este metodo no provee acceso a los parametros de salida.
        /// 
        /// Ejemplo:  
        ///  int orderCount = (int)ExecuteScalar(conn, "GetOrderCount", 24, 36);
        /// </remarks>
        /// <param name="connectionInfo">Un <see cref="IPostgreSqlConnectionInfo"/> Valido</param>
        /// <param name="spName">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="parameterValues">Una coleccion de <see cref="object"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="object"/> que contiene el valor del conjunto de resultados 1x1 generado por el comando</returns>
        public static object ExecuteScalar(IPostgreSqlConnectionInfo connectionInfo, string spName, params object[] parameterValues)
        {
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) && (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                NpgsqlParameter[] commandParameters = PostgreSqlHelperParameterCache.GetSpParameterSet(connectionInfo, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteScalar(connectionInfo, CommandType.StoredProcedure, spName, commandParameters);
            }
            //otherwise we can just call the SP without params
            else
            {
                return ExecuteScalar(connectionInfo, CommandType.StoredProcedure, spName);
            }
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> (que devuelve un conjunto de resultados 1x1) utilizando un <see cref="NpgsqlTransaction"/> especificado. 
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int orderCount = (int)ExecuteScalar(trans, CommandType.StoredProcedure, "GetOrderCount");
        /// </remarks>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <returns>Un <see cref="object"/> que contiene el valor del conjunto de resultados 1x1 generado por el comando</returns>
        public static object ExecuteScalar(NpgsqlTransaction transaction, CommandType commandType, string commandText)
        {
            return ExecuteScalar(transaction, commandType, commandText, (NpgsqlParameter[])null);
        }

        /// <summary>
        ///Ejecuta un <see cref="NpgsqlCommand"/> (que devuelve un conjunto de resultados 1x1) utilizando un <see cref="NpgsqlTransaction"/> especificado. 
        /// </summary>
        /// <remarks>
        /// Ejemplo:  
        ///  int orderCount = (int)ExecuteScalar(trans, CommandType.StoredProcedure, "GetOrderCount", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> Valido</param>
        /// <param name="commandType">El tipo de comando a ejecutar (stored procedure, text, etc.</param>
        /// <param name="commandText">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="commandParameters">Una coleccion de <see cref="NpgsqlParameter"/> a utilizar en el comando</param>
        /// <returns>Un <see cref="object"/> que contiene el valor del conjunto de resultados 1x1 generado por el comando</returns>
        public static object ExecuteScalar(NpgsqlTransaction transaction, CommandType commandType, string commandText, params NpgsqlParameter[] commandParameters)
        {
            //Creamos y preparamos el comando
            NpgsqlCommand cmd = new NpgsqlCommand();
            object retval = null ;
            try
            {
                PrepareCommand(cmd, transaction.Connection, transaction, commandType, commandText, commandParameters);
                //Ejecutamos el comando
                retval = cmd.ExecuteScalar();
            }
            catch (NpgsqlException ex)
            {
                Console.Write(string.Format("{0}{1}", strMensajeErrorComando, ex.Message));
            }
            finally
            {
                // Liberamos la coleccion de parametros
                cmd.Parameters.Clear();
            }
            return retval;
        }

        /// <summary>
        /// Ejecuta un <see cref="NpgsqlCommand"/> (que devuelve un conjunto de resultados 1x1) utilizando un <see cref="NpgsqlTransaction"/> especificado.
        /// Este metodo consulta a la base de datos la informacion de los parametros y los asigna su valores
        /// (La primera ver que es llamado).
        /// </summary>
        /// <remarks>
        /// Este metodo no provee acceso a los parametros de salida.
        /// 
        /// Ejemplo:  
        ///  int orderCount = (int)ExecuteScalar(trans, "GetOrderCount", 24, 36);
        /// </remarks>
        /// <param name="connectioInfo">Un <see cref="IPostgreSqlConnectionInfo"/> Valido</param>
        /// <param name="transaction">Un <see cref="NpgsqlTransaction"/> Valido</param>
        /// <param name="spName">El nombre del Procedimiento almacenado o Comando T-SQL</param>
        /// <param name="parameterValues">aUna coleccion de <see cref="object"/> con los valores de los parametros a utilizar en el comando</param>
        /// <returns>Un <see cref="object"/> que contiene el valor del conjunto de resultados 1x1 generado por el comando</returns>
        public static object ExecuteScalar(IPostgreSqlConnectionInfo connectioInfo, NpgsqlTransaction transaction, string spName, params object[] parameterValues)
        {
            //si recibimos los parametros
            if ((parameterValues != null) && (parameterValues.Length > 0))
            {
                // recuepramos lso valores de la memoria cachet o lo recuperamos desde la base de datos.
                NpgsqlParameter[] commandParameters = PostgreSqlHelperParameterCache.GetSpParameterSet(connectioInfo, spName);

                // asignamos los valores de los parametros a la base de datos.
                AssignParameterValues(commandParameters, parameterValues);

                //ejecutamos el comando.
                return ExecuteScalar(transaction, CommandType.StoredProcedure, spName, commandParameters);
            }
            else
            {
                return ExecuteScalar(transaction, CommandType.StoredProcedure, spName);
            }
        }
        #endregion ExecuteScalar	

        public static NpgsqlParameter CreateParameter(string paramName, NpgsqlDbType tipo, int len, object value) {
            NpgsqlParameter p = new NpgsqlParameter( paramName ,tipo,len);
            p.Value = value ;
            return p;        
        }
    }
}